﻿using System.Diagnostics;
using System.Net.Sockets;
using System.Text;

namespace MPNet6.Client.Receive;
public enum EMPEventType
{
    Connecting,
    Connectted,
    Reconnectted,
    Failure,
    /// <summary>
    /// 多条消息以\t分隔
    /// </summary>
    Message,
    IsRuning,
    Stopped
}
public record MpArgs(EMPEventType MPEventType, string? Message = default)
{
    public override string ToString()
    {
        return string.IsNullOrEmpty(Message) ? $" {MPEventType}" : $" {MPEventType}:{Message}";
    }
}

public class MPClientReceive
{
    const int ReceiveBufferSize = 64 * 1024;
    const int SendBufferSize = 1024;
    const byte MoreConnectFlag = 0;
    const byte MoreReconnectFlag = 1;
    const byte OnceConnectFlag = 2;
    const byte OnceReconnectFlag = 3;
    const byte MoreEchoFlag = 1;
    const byte OnceEchoFlag = 3;

    private readonly string _Host;
    private readonly int _Port;
    private readonly int _SecondsDelay;
    private readonly byte[] _ConnectionBuffer;
    private readonly byte[] _EchoBuffer;

    private CancellationTokenSource _StopTokenSource = default!;
    private CancellationToken _StopToken = default!;

    private bool _Runing = false;

    private TcpClient? _TcpClient;
    private NetworkStream? _Stream;

    public event Action<MpArgs>? EventMP;



    /// <summary>
    /// 
    /// </summary>
    /// <param name="host">主机名称</param>
    /// <param name="port">端口号</param>
    /// <param name="clientId">客户端Id</param>
    /// <param name="secondsDelay">等待1条消息的秒数(Once)；0：表示一直在线，可接收多条消息(More)</param>
    public MPClientReceive(string host, int port, string clientId, int secondsDelay)
    {
        _Host = host;
        _Port = port;
        _SecondsDelay = secondsDelay;
        _ConnectionBuffer = Encoding.ASCII.GetBytes($"\0{clientId}");
        if (_SecondsDelay == 0)
        {
            _ConnectionBuffer[0] = MoreConnectFlag;
            _EchoBuffer = new byte[] { MoreEchoFlag };
        }
        else
        {
            _ConnectionBuffer[0] = OnceConnectFlag;
            _EchoBuffer = new byte[] { OnceEchoFlag };
        }
    }




    public void Start()
    {
        if (_Runing)
        {
            EventMP?.Invoke(new(EMPEventType.IsRuning));
            return;
        }
        _Runing = true;
        _StopTokenSource = _SecondsDelay > 0 ? new(TimeSpan.FromSeconds(_SecondsDelay)) : new();
        _StopToken = _StopTokenSource.Token;

        Connect(false);
    }


    private void Connect(bool isReconnect)
    {
        if (isReconnect)
        {
            if (_SecondsDelay == 0)
                _ConnectionBuffer[0] = MoreReconnectFlag;
            else
                _ConnectionBuffer[0] = OnceReconnectFlag;
        }

        Task.Run(async () =>
        {
            if (await ConnectAsync())
                await ReceiveAsync();
        });
    }


    /// <summary>
    /// 
    /// </summary>
    /// <returns>StopToken.Cancel=>false,Connected=>true</returns>
    private async Task<bool> ConnectAsync()
    {

        _TcpClient?.Close();
        _Stream?.Close();
        while (true)
        {
            EventMP?.Invoke(new MpArgs(EMPEventType.Connecting));
            try
            {
                _TcpClient = new TcpClient()
                {
                    ReceiveBufferSize = ReceiveBufferSize,
                    SendBufferSize = SendBufferSize
                };
                Keepalive(_TcpClient.Client);
                await _TcpClient.ConnectAsync(_Host, _Port, _StopToken);
                _Stream = _TcpClient.GetStream();
                await _Stream.WriteAsync(_ConnectionBuffer);
                return true;
            }
            catch (OperationCanceledException)
            {
                Stop();
                return false;
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Connect:" + ex.Message);

                _Stream?.Close();
                _TcpClient?.Close();
                try
                {
                    await Task.Delay(1000, _StopToken);
                }
                catch
                {
                    return false;
                }
            }
        }


    }
    private async Task ReceiveAsync()
    {
        var buffer = new byte[ReceiveBufferSize];
        bool needReconnect = false;
        try
        {

            while (true)
            {
                try
                {

                    #region 别设置断点，服务器等待回复，2秒钟没得到回复，认为断线
                    var len = await _Stream!.ReadAsync(buffer, 0, buffer.Length, _StopToken);
                    if (len <= 0)
                    {
                        needReconnect = true;
                        return;
                    }
                    await _Stream.WriteAsync(_EchoBuffer);
                    #endregion

                    switch (buffer[0])
                    {
                        case 0: //Connect More
                            {
                                EventMP?.Invoke(new MpArgs(EMPEventType.Connectted));
                                break;
                            }
                        case 1: //Connect More
                            {
                                EventMP?.Invoke(new MpArgs(EMPEventType.Reconnectted));
                                break;
                            }
                        case 2: //Connect Once
                            {
                                EventMP?.Invoke(new MpArgs(EMPEventType.Connectted));
                                break;
                            }
                        case 3: //Reconnect Once
                            {
                                EventMP?.Invoke(new MpArgs(EMPEventType.Reconnectted));
                                break;
                            }

                        case 100: //Connect to Server Error
                            EventMP?.Invoke(new MpArgs(EMPEventType.Failure, Encoding.ASCII.GetString(buffer, 1, len - 1)));
                            return;
                        case 200://Echo
                            break;
                        default://Message(s),用 '\t'隔开;
                            {
                                EventMP?.Invoke(new(EMPEventType.Message, Encoding.ASCII.GetString(buffer, 0, len)));
                                if (_SecondsDelay > 0)
                                    return;
                                break;
                            }

                    }
                }
                catch (OperationCanceledException)
                {
                    return;
                }
                catch (Exception ex)
                {
                    EventMP?.Invoke(new MpArgs(EMPEventType.Failure,  ex.Message));
                    needReconnect = true;
                    return;

                }
            }
        }
        finally
        {
            if (needReconnect)
                Connect(true);
            else
                Stop();
        }
    }

    private void Keepalive(Socket socket)
    {
        var tcpKeepalive = new byte[12];
        uint keepAliveTimeMilliSeconds = 2000;
        uint keepAliveIntervalMilliSeconds = 1000;
        BitConverter.GetBytes((uint)1).CopyTo(tcpKeepalive, 0);//switch on
        BitConverter.GetBytes(keepAliveTimeMilliSeconds).CopyTo(tcpKeepalive, 4);//最时候一次发送或接收数据后4秒后，开始联系服务器
        BitConverter.GetBytes(keepAliveIntervalMilliSeconds).CopyTo(tcpKeepalive, 8);//若对话没有回复，则每隔8秒联系服务器1次，若干次（5次？）都没有回复，则Stream!.ReadAsync会报异常
        socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
#pragma warning disable CA1416 // 验证平台兼容性
        socket.IOControl(IOControlCode.KeepAliveValues, tcpKeepalive, null);
#pragma warning restore CA1416 // 验证平台兼容性
    }

    public void Stop()
    {
        if (_Runing)
        {
            _Runing = false;
            _StopTokenSource?.Cancel();
            _Stream?.Close();
            _TcpClient?.Close();
            EventMP?.Invoke(new MpArgs(EMPEventType.Stopped));
        }
    }
}